#!/usr/local/bin/thrift -cpp -java -py -php -rb -perl -erl -xsd -r
#
# Thrift Tutorial
# Mark Slee (mcslee@facebook.com)
#
# This file aims to teach you how to use Thrift, in a .thrift file. Neato. The
# first thing to notice is that .thrift files support standard shell comments.
# This lets you make your thrift file executable and include your Thrift build
# step on the top line. And you can place comments like this anywhere you like.
#
# Before running this file, you will need to have installed the thrift compiler
# into /usr/local/bin.

/**
 * The first thing to know about are types. The available types in Thrift are:
 *
 *  bool        Boolean, one byte
 *  byte        Signed byte
 *  i16         Signed 16-bit integer
 *  i32         Signed 32-bit integer
 *  i64         Signed 64-bit integer
 *  double      64-bit floating point value
 *  string      String
 *  map<t1,t2>  Map from one type to another
 *  list<t1>    Ordered list of one type
 *  set<t1>     Set of unique elements of one type
 *
 * Did you also notice that Thrift supports C style comments?
 */

// Just in case you were wondering... yes. We support simple C comments too.

/**
 * Thrift files can reference other Thrift files to include common struct
 * and service definitions. These are found using the current path, or by
 * searching relative to any paths specified with the -I compiler flag.
 *
 * Included objects are accessed using the name of the .thrift file as a
 * prefix. i.e. shared.SharedObject
 */
//include "shared.thrift"

/**
 * Thrift files can namespace, package, or prefix their output in various
 * target languages.
 */
namespace cpp ringer
namespace java ringer
namespace php ringer
namespace perl ringer

typedef string rpath_t
typedef string block_value_t
typedef string rkeyword_t
typedef i64 simresult_t

// base size to break files up into
const i64 RINGER_BLOCK_SIZE =  65536 

/* a user lease can apply to all users in a group, if this value
  is used for a user */
const i32 NO_USER = -1 

/* Or, a lease can apply to only 1 particular user in a group */
const i32 NO_GROUP = -1

/* Also, groups can extend byond a single host, using this value */
const string NO_HOST_NAME = "RINGER_NO_HOST"
const i32 NO_HOST_PORT = 0

const string NO_PUBLIC_KEY = ""

// A host is uniquly identified by a host name and a port
struct host_t {
  1: string host_name = NO_HOST_NAME,
  2: i32 host_port = NO_HOST_PORT
}

/**
 * You can define enums, which are just 32 bit integers. Values are optional
 * and start at 1 if not supplied, C style again.
 * Here, we have all of the types of RNodes
 */
enum RNodeType {
  RNODE = 0,
  RFILE = 1,
  RDIRECTORY = 2
}

// Store a basic time value here, secounds and nsecounds.
struct TimeStr {
  1: i64 time_sec,
  2: i64 time_nsec
}

// Uniquly idenifies a user and/or group in the Ringer system.
struct UserInfo {
  1: host_t host = {},
  2: i32 uid = NO_USER,
  3: i32 gid = NO_GROUP,
  4: string public_key = NO_PUBLIC_KEY,
  5: i32 ringer_uid      // Uniquly identifies this user accross the ringer system
}

// all of the metadata about a file is defined here
struct RNode {
  1: rpath_t path,
  2: rpath_t parent_path ,
  3: i32 mode,
  4: i32 nlink ,
  5: i32 uid  ,
  6: i32 gid  ,
  7: TimeStr atime,
  8: TimeStr mtime,
  9: TimeStr ctime,
  10: i32 offset ,
  11: i32 number_blocks  ,
  12: i64 blksize  ,
  13: i64 flags  ,
  14: i64 gen,
  15: set<rpath_t> files,
  16: set<i32> readers,
  17: i32 writer,
  18: map<string, string> xattrs,
  19: RNodeType node_type = RNODE,  
  20: rpath_t fuse_path,
  21: i32 st_dev,
  22: i32 st_ino,
  23: i32 st_rdev,
  24: UserInfo owner,
  25: host_t mds_owner,
  26: set<rkeyword_t> keywords,
  27: list<simresult_t> simhash_keys
}

// this is the basic file transport means
struct RingerBlock {
  1: i64 block_size = RINGER_BLOCK_SIZE,
  2: block_value_t block_value
}

// How we specify a search
struct RingerSearch {
  1: set<rkeyword_t> search_terms,
  2: list<simresult_t> simhash_keys
}

/**
 * Structs can also be exceptions, if they are nasty.
 */
exception InvalidOperation {
  1: i32 what,
  2: string why
}

/**
 * Ahh, now onto the cool part, defining a service. Services just need a name
 * and can optionally inherit from another service using the extends keyword.
 *   
 */
service RingerMetaServer {

  /**
   * A method definition looks like C code. It has a return type, arguments,
   * and optionally a list of exceptions that it may throw. Note that argument
   * lists and exception lists are specified using the exact same syntax as
   * field lists in struct or exception definitions.
   */

  // for testing only
  i32 ping(),
  i32 add(1:i32 num1, 2:i32 num2),

  // metadata manipulation
  RNode get(1:rpath_t path, 2:UserInfo userInfo) throws (1:InvalidOperation ouch),
  async void put(1:RNode node, 2:UserInfo userInfo),
  async void remove(1:rpath_t path, 2:UserInfo userInfo),

  // convienince methods for concurrany
  bool get_read(1:rpath_t path, 2:UserInfo userInfo) throws (1:InvalidOperation ouch),
  async void end_read(1:rpath_t path, 2:UserInfo userInfo),
  bool get_write(1:rpath_t path, 2:UserInfo userInfo) throws (1:InvalidOperation ouch),
  async void end_write(1:rpath_t path, 2:UserInfo userInfo),
  
  // data transfer methods
  // send in 1 syched method
  RingerBlock get_block(1:rpath_t path, 2:UserInfo userInfo, 3: i32 offset, 4: i32 length) throws (1:InvalidOperation ouch),
  
  // or, do it in two async ones
  async void send_block(1:UserInfo userInfo, 2:rpath_t path, 3: i32 offset, 4: i32 length)
  async void accept_block(1:RingerBlock ret_val, 2:rpath_t path, 3: i32 offset, 4: i32 length, 5:UserInfo userInfo)
 
   // alert a mds that client exists
  async void connect_mds(1:UserInfo ui)
  
  // resign a client from a mds
  async void disconnect_mds(1:UserInfo ui)
    
  // alert a parent mds that you exist
  async void connect_parent(1:host_t child_info)
  
  // resign from a parent mds
  async void disconnect_parent(1:host_t child_info)
  
  // get owner info from a path
  host_t get_mds_owner(1:rpath_t path)

  // Get where keyword mapped
  host_t get_mds_keyholder(1:rkeyword_t keyword)

  // Publish that you are the keyholder
  async void publish_mds_keyholder(1:rkeyword_t keyword, 2:host_t keyholder)
  
  // No longer the keyholder
  async void relinquish_mds_keyholder(1:rkeyword_t keyword, 2:host_t keyholder)
  
  // alert parent of child's new rnode
  async void alert_parent_of_rnode(1:rpath_t path, 2:host_t owner_info)
  
  // alert parent of child's deleted rnode
  async void alert_parent_of_removed_rnode(1:rpath_t path)
  
  set<string> update_hosts()
  
  // ask for files matching the given tag
  set<rpath_t> search(1:RingerSearch search)
  
  // tell about file having given tag
  async void alert_tagged_file(1: rpath_t path, 2:rkeyword_t keyword )
}
